#import "BWGraphDocument.h"
#import <BSL/BSL.h>

#import "BWDocumentMetadata.h"

#import "BWFabric.h"
#import "BWGridGeometry.h"
#import "BWGridAttributes.h"

#import "BWLayerView.h"

#import "BWLayerSet.h"
#import "BWBackStitchLayer.h"
#import "BWCrossStitchLayer.h"
#import "BWGridLayer.h"
#import "BWSelectionLayer.h"
#import "BWSelectionLayer.h"

#import "BWThread.h"
#import "BWThreadColor.h"

#import "BWAppController.h"
#import "BWColorset.h"
#import "BWColorSearchAndReplaceController.h"

#import "BWSymbolCell.h"
#import "BWColorKey.h"


#import "BWHandTool.h"
#import "BWCrossStitchDrawTool.h"
#import "BWCrossStitchLineTool.h"
#import "BWCrossStitchCircleTool.h"
#import "BWCrossStitchEraserTool.h"
#import "BWCrossStitchRectangleTool.h"
#import "BWCrossStitchPaintbucketTool.h"
#import "BWSelectionTool.h"
#import "BWBackStitchDrawTool.h"
#import "BWBackStitchEditingTool.h"
#import "BWPartialStitchTool.h"
#import "BWPartialStitchWaterdropTool.h"

NSString *BWGraphDocument_DocumentDeactivateNotification
    = @"BWGraphDocument Document Deactivate Notification";

NSString *BWGraphDocument_DocumentActivateNotification
    = @"BWGraphDocument Document Activate Notification";

NSString *BWGraphDocument_ToolChangeNotification
    = @"BWGraphDocument Tool Change Notification";

// !!! total hack for bootstrapping layers
BWGraphDocument *g_globalDocument;

@interface BWGraphDocument(ForwardReferences)

- (void) handleTools: (id) sender;

@end // ForwardReference


@implementation BWGraphDocument

- (void) setupDefaultMetadata
{
    BOOL presetAuthor;
    presetAuthor = [[NSUserDefaults standardUserDefaults] 
                       boolForKey: @"presetAuthor"];

    if (presetAuthor) {
        [metadata setAuthor: (NSString *)CSCopyUserName(FALSE)];
    }

    NSString *string;
    string = [[NSUserDefaults standardUserDefaults] 
                 stringForKey: @"defaultCopyright"];
    if (string) {
        [metadata setCopyright: string];
    }

} // setupDefaultMetadata


- (id) init
{
    if (self = [super init]) {
        metadata = [[BWDocumentMetadata alloc] init];
        [self setupDefaultMetadata];

        fabric = [[BWFabric alloc] init];
        [fabric setWidth: 2]; // 5, 6
        [fabric setHeight: 2];
        [fabric setCount: 10];
        [fabric setOverX: 1];
        
        controllingGeometry = [[BWGridGeometry alloc] init];
        [controllingGeometry setCellWidth: 12];
        [controllingGeometry setCellHeight: 12];
        [controllingGeometry setFabric: fabric];

        gridAttributes = [[BWGridAttributes alloc] init];
        [gridAttributes setMinorLineWeight: kWeight_1_3_pt];
        [gridAttributes setMinorLineColor: kColor_Gray];
        [gridAttributes setMajorLineWeight: kWeight_1_pt];
        [gridAttributes setMajorLineColor: kColor_DarkGray];
        [gridAttributes setShowMajorLines: YES];
        [gridAttributes setMajorLinesEvery: 10];

        colorKey = [[BWColorKey alloc] init];
        [colorKey setDocument: self];
    }

    return (self);

} // init


- (void) dealloc
{
    [metadata release];
    [colorKey release];
    [gridAttributes release];
    [controllingGeometry release];
    [fabric release];

    [super dealloc];

} // dealloc


- (NSString *) windowNibName
{
    return (@"BWGraphDocument");
} // windowNibName


- (void) stubOutLayers
{
    g_globalDocument = self;
    srandom (time(NULL));

    if (!documentFromFile) {

        BWColorset *colorset = [[[BWAppController globalController]
                                    colorsets] objectAtIndex: 0];
        NSArray *symbols = [[BWAppController globalController] symbols];
        
        BWThread *thread;
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 23] copy]];
        [thread setSymbol: [symbols objectAtIndex: 0]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 42] copy]];
        [thread setSymbol: [symbols objectAtIndex: 1]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 87] copy]];
        [thread setSymbol: [symbols objectAtIndex: 2]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 99] copy]];
        [thread setSymbol: [symbols objectAtIndex: 3]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 2] copy]];
        [thread setSymbol: [symbols objectAtIndex: 4]];
        [colorKey addThread: thread];
        [thread release];
        
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 317] copy]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 330] copy]];
        [colorKey addThread: thread];
        [thread release];
        
        thread = [[BWThread alloc] init];
        
        [thread setColor: [[colorset colorAtIndex: 400] copy]];
        [colorKey addThread: thread];
        [thread release];
        
        
        [self willChangeValueForKey: @"colorKey"];
        [self didChangeValueForKey: @"colorKey"];

        BWLayer *lastLayer = nil;
        layers = [[BWLayerSet alloc] init];

        BWSelectionLayer *sellayer;

        sellayer = [[BWSelectionLayer alloc] init];
        [sellayer setGeometry: controllingGeometry];
        [sellayer setName: @"selection"];
        [layers addLayer: sellayer];
        [sellayer setNextHigherLayer: lastLayer];
        lastLayer = sellayer;

        BWBackStitchLayer *bslayer;

        bslayer = [[BWBackStitchLayer alloc] init];
        [bslayer setGeometry: controllingGeometry];
        [bslayer setName: @"backstitch 1"];
        [layers addLayer: bslayer];
        [bslayer setNextHigherLayer: lastLayer];
        lastLayer = bslayer;
        
        bslayer = [[BWBackStitchLayer alloc] init];
        [bslayer setGeometry: controllingGeometry];
        [bslayer setName: @"backstitch 2"];
        [layers addLayer: bslayer];
        [bslayer setNextHigherLayer: lastLayer];
        lastLayer = bslayer;
        

        BWCrossStitchLayer *layer;

        layer = [[BWCrossStitchLayer alloc] init];
        [layer setGeometry: controllingGeometry];
        [layer setName: @"thing 1"];
        [layers addLayer: layer];
        [layer setNextHigherLayer: lastLayer];
        lastLayer = layer;

        layer = [[BWCrossStitchLayer alloc] init];
        [layer setGeometry: controllingGeometry];
        [layer setName: @"thing 2"];
        [layers addLayer: layer];
        [layer setNextHigherLayer: lastLayer];
        lastLayer = layer;

        layer = [[BWCrossStitchLayer alloc] init];
        [layer setGeometry: controllingGeometry];
        [layer setName: @"thing 3"];
        [layers addLayer: layer];
        [layer setNextHigherLayer: lastLayer];
        lastLayer = layer;

        BWGridLayer *gridLayer;
        gridLayer = [[BWGridLayer alloc] init];
        [gridLayer setGeometry: controllingGeometry];
        [gridLayer setName: @"grid"];
        [layers addLayer: gridLayer];
        [gridLayer setNextHigherLayer: lastLayer];
        [gridLayer setGridAttributes: gridAttributes];
        lastLayer = gridLayer;
    }

} // stubOutLayers


- (void) windowControllerDidLoadNib: (NSWindowController *) aController
{
    [super windowControllerDidLoadNib: aController];

    // set up the centering clip view
    id centeringClipView;
    centeringClipView = [[BWCenteringClipView alloc]
                            initWithScrollView: layerViewScrollView];
    
    [centeringClipView setBackgroundColor: [NSColor whiteColor]];
    [centeringClipView release];

    [layerViewScrollView setDocumentView: layerView];

    // rest of the startup

    [self stubOutLayers];

    [layerView setFabric: fabric];
    [layerView setLayers: layers];

    NSTableColumn *column;
    column = [colorKeyTableView tableColumnWithIdentifier: @"symbolColumn"];
    [column setDataCell: [[BWSymbolCell alloc] init]];

    [tools setSegmentCount: 17];
    [tools setTarget: self];
    [tools setAction: @selector(handleTools:)];

    [tools setWidth: 24  forSegment: 0];
    [tools setTag: 0  forSegment: 0];
    [tools setImage: [[NSCursor arrowCursor] image]
           forSegment: 0];

    [tools setWidth: 24  forSegment: 1];
    [tools setTag: 1  forSegment: 1];
    [tools setImage: [NSImage imageNamed: @"line-tool"]
           forSegment: 1];

    [tools setWidth: 24  forSegment: 2];
    [tools setTag: 2  forSegment: 2];
    [tools setImage: [NSImage imageNamed: @"rect-tool"]
           forSegment: 2];

    [tools setWidth: 24  forSegment: 3];
    [tools setTag: 3  forSegment: 3];
    [tools setImage: [NSImage imageNamed: @"circle-tool"]
           forSegment: 3];

    [tools setWidth: 24  forSegment: 4];
    [tools setTag: 4  forSegment: 4];
    [tools setImage: [NSImage imageNamed: @"eraser-tool"]
           forSegment: 4];

    [tools setWidth: 24  forSegment: 5];
    [tools setTag: 5  forSegment: 5];
    [tools setImage: [[NSCursor crosshairCursor] image]
           forSegment: 5];

    [tools setWidth: 24  forSegment: 6];
    [tools setTag: 6  forSegment: 6];
    [tools setImage: [NSImage imageNamed: @"paintbucket-tool"]
           forSegment: 6];

    [tools setWidth: 24  forSegment: 7];
    [tools setTag: 7  forSegment: 7];
    [tools setImage: [NSImage imageNamed: @"backstitch-tool"]
           forSegment: 7];

    [tools setWidth: 6  forSegment: 8];
    [tools setTag: 8  forSegment: 8];
    [tools setEnabled: NO  forSegment: 8];

    [tools setWidth: 24  forSegment: 9];
    [tools setTag: 9  forSegment: 9];
    [tools setImage: [NSImage imageNamed: @"x-stitch"]
           forSegment: 9];

    [tools setWidth: 24  forSegment: 10];
    [tools setTag: 10  forSegment: 10];
    [tools setImage: [NSImage imageNamed: @"partial-stitch-nw-tool"]
           forSegment: 10];

    [tools setWidth: 24  forSegment: 11];
    [tools setTag: 11  forSegment: 11];
    [tools setImage: [NSImage imageNamed: @"partial-stitch-sw-tool"]
           forSegment: 11];

    [tools setWidth: 24  forSegment: 12];
    [tools setTag: 12  forSegment: 12];
    [tools setImage: [NSImage imageNamed: @"partial-stitch-ne-tool"]
           forSegment: 12];

    [tools setWidth: 24  forSegment: 13];
    [tools setTag: 13  forSegment: 13];
    [tools setImage: [NSImage imageNamed: @"partial-stitch-se-tool"]
           forSegment: 13];

    [tools setWidth: 24  forSegment: 14];
    [tools setTag: 14  forSegment: 14];
    [tools setImage: [NSImage imageNamed: @"waterdrop-tool"]
           forSegment: 14];

    [tools setWidth: 6  forSegment: 15];
    [tools setTag: 15  forSegment: 15];
    [tools setEnabled: NO  forSegment: 15];

    [tools setWidth: 24  forSegment: 16];
    [tools setTag: 16  forSegment: 16];
    [tools setImage: [[NSCursor openHandCursor] image]
           forSegment: 16];

    [[BWAppController globalController]
        showColorKeyWindow: self];
    
} // windowControllerDidLoadNib


- (NSData *) dataRepresentationOfType: (NSString *) aType
{
    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

    [dict setObject: metadata
          forKey: @"metadata"];
    [dict setObject: fabric
          forKey: @"fabric"];
    [dict setObject: colorKey
          forKey: @"colorKey"];
    [dict setObject: layers
          forKey: @"layers"];
    [dict setObject: gridAttributes
          forKey: @"gridAttributes"];

    NSData *data;
    data = [NSArchiver archivedDataWithRootObject: dict];

    [dict release];

    return (data);

} // dataRepresentationOfType



- (BOOL) loadDataRepresentation: (NSData *) data 
                         ofType: (NSString *) type
{
    documentFromFile = YES;

    NSDictionary *dict = [NSUnarchiver unarchiveObjectWithData: data];

    [metadata release];
    metadata = [dict objectForKey: @"metadata"];
    [metadata retain];

    [fabric release];
    fabric = [dict objectForKey: @"fabric"];
    [fabric retain];
    [controllingGeometry setFabric: fabric];

    [colorKey release];
    colorKey = [dict objectForKey: @"colorKey"];
    [colorKey retain];

    [layers release];
    layers = [dict objectForKey: @"layers"];
    [layers retain];

    [layers setGeometry: controllingGeometry];

    [gridAttributes release];
    gridAttributes = [dict objectForKey: @"gridAttributes"];
    [gridAttributes retain];

    [colorKey setDocument: self];

    return (YES);

} // loadDataRepresentation


- (IBAction) handleZoom: (id) sender
{
    int tag = [[sender selectedItem] tag];

    float factor = tag / 100.0;

    [layerView setZoom: factor];

} // handleZoom


- (IBAction) handleDisplayType: (id) sender
{
    int tag = [[sender selectedItem] tag];

    NSEnumerator *enumerator = [layers layerEnumerator];

    BWLayer *layer;
    while (layer = [enumerator nextObject]) {
        if ([layer layerKind] == kLayerKind_CrossStitch) {
            BWCrossStitchLayer *cslayer;
            cslayer = (BWCrossStitchLayer*) layer;
            [cslayer setDisplayType: tag];
        }
    }
    
    [layerView setNeedsDisplay: YES];

} // handleDisplayType



- (void) postNotification: (NSString *) notificationName
{
    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
    
    [center postNotificationName: notificationName
            object: self];

} // postNotification


// these get called because we're the document window's delegate

- (void) windowDidBecomeMain: (NSNotification *) notification
{
    [self postNotification: BWGraphDocument_DocumentActivateNotification];
    g_globalDocument = self;

} // windowDidBecomeMain


- (void) windowDidResignMain: (NSNotification *) notification
{
    [self postNotification: BWGraphDocument_DocumentDeactivateNotification];

} // windowDidResignMain


- (void) windowWillClose: (NSNotification *) notification
{
    [self postNotification: BWGraphDocument_DocumentDeactivateNotification];

} // windowDidClose


- (BWColorKey *) colorKey
{
    return (colorKey);
} // colorKey


- (void) redraw
{
    [layerView setNeedsDisplay: YES];
} // redraw


- (void) handleTools: (id) sender
{
    int tag = [sender tagForSelectedSegment];
    BWTool *tool = nil;

    if (tag == 0) {
        tool = [[BWBackStitchEditingTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }

    if (tag == 1) {
        tool = [[BWCrossStitchLineTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 2) {
        tool = [[BWCrossStitchRectangleTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 3) {
        tool = [[BWCrossStitchCircleTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 4) {
        tool = [[BWCrossStitchEraserTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 5) {
        tool = [[BWSelectionTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 6) {
        tool = [[BWCrossStitchPaintbucketTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }
    
    if (tag == 7) {
        tool = [[BWBackStitchDrawTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }

    if (tag == 9) {
        tool = [[BWCrossStitchDrawTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }

    if (tag >= 10 && tag <= 13) {
        BWPartialStitchTool_Direction direction;

        // partial stitches
        switch (tag) {
        case 10:
            direction = kDirection_NW;
            break;
        case 11:
            direction = kDirection_SW;
            break;
        case 12:
            direction = kDirection_NE;
            break;
        case 13:
            direction = kDirection_SE;
            break;
        }
        tool = [[BWPartialStitchTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView
                   direction: direction];
    }

    if (tag == 14) {
        tool = [[BWPartialStitchWaterdropTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }

    if (tag == 16) {
        tool = [[BWHandTool alloc]
                   initWithGeometry: controllingGeometry
                   layers: layers
                   layerView: layerView];
    }

    
    [tool autorelease];
    [layerView setTool: tool];

    currentTool = tool;
    [self postNotification: BWGraphDocument_ToolChangeNotification];

    [statusLine setStringValue: [tool name]];
        
} // handleTools



// scaffolding

- (void) addStitches: (id) sender
{
    int i;
    for (i = 0; i < [layers count]; i++) {
        BWCrossStitchLayer *layer = (id)[layers layerAtIndex: i];
        
        if ([layer layerKind] == kLayerKind_CrossStitch
            && [layer active]) {
            [layer addStitchesForDebugging];
        }
    }

} // addStitches


- (void) addBackStitches: (id) sender
{
    int i;
    for (i = 0; i < [layers count]; i++) {
        BWBackStitchLayer *layer = (id)[layers layerAtIndex: i];
        
        if ([layer layerKind] == kLayerKind_BackStitch
            && [layer active]) {
            [layer addStitchesForDebugging];
        }
    }

} // addBackStitches



- (void) addLayerOfKind: (BWLayerKind) layerKind
               withName: (NSString *) layerName
{
    // find place to put this
    BOOL inKind = NO;
    int i;
    BWLayer *lastKindLayer = nil;

    for (i = 0; i < [layers count]; i++) {
        BWLayer *layer;
        layer = [layers layerAtIndex: i];
        if (!inKind) {
            if ([layer layerKind] == layerKind) {
                lastKindLayer = layer;
                inKind = YES;
            }
        } else {
            if ([layer layerKind] != layerKind) {
                break;
            }
        }
    }

    if (lastKindLayer != nil) {
        BWLayer *layer = nil;

        if (layerKind == kLayerKind_BackStitch) {
            layer = [[BWBackStitchLayer alloc] init];
        } else if (layerKind == kLayerKind_CrossStitch) {
            layer = [[BWCrossStitchLayer alloc] init];
        }

        [layer setGeometry: controllingGeometry];
        [layer setName: layerName];
        [layers addLayer: layer
                atIndex: i];

        [layer setNextHigherLayer: lastKindLayer];
        [layer release];
    }

} // addLayerOfKind



- (IBAction) addBackStitchLayer: (id) sender
{
    static int s_layerNumber = 1;
    NSString *layerName;
    layerName = [NSString stringWithFormat: @"Backstitch Layer %d", s_layerNumber];
    s_layerNumber++;

    [self addLayerOfKind: kLayerKind_BackStitch
          withName: layerName];

} // addBackStitchLayer



- (IBAction) addCrossStitchLayer: (id) sender
{
    static int s_layerNumber = 1;
    NSString *layerName;
    layerName = [NSString stringWithFormat: @"Cross stitch Layer %d", s_layerNumber];
    s_layerNumber++;
    
    [self addLayerOfKind: kLayerKind_CrossStitch
          withName: layerName];

} // addCrossStitchLayer


- (void) searchAndReplaceWindowDidEnd: (NSWindow *) sheet
                           returnCode: (int) returnCode
                          contextInfo: (void *) contextInfo
{
    BWColorSearchAndReplaceController *controller;
    controller = (BWColorSearchAndReplaceController *) contextInfo;

    if (returnCode == NSOKButton) {
        BWThread *search, *replace;
        search = [controller searchThread];
        replace = [controller replaceThread];

        if ([controller replaceCrossStitches]) {
            NSEnumerator *enumerator = [layers activeLayerEnumeratorForKind:
                                                   kLayerKind_CrossStitch];
            BWCrossStitchLayer *layer;
            while (layer = [enumerator nextObject]) {
                [layer replaceThread: search  withThread: replace];
            }
        }

        if ([controller replaceBackStitches]) {
            NSEnumerator *enumerator = [layers activeLayerEnumeratorForKind:
                                                   kLayerKind_BackStitch];
            BWCrossStitchLayer *layer;
            while (layer = [enumerator nextObject]) {
                [layer replaceThread: search  withThread: replace];
            }
        }
    }

    [sheet orderOut: self];
    [controller release];

} // sheetDidEnd


- (IBAction) searchAndReplaceColors: (id) sender
{
    BWColorSearchAndReplaceController *controller;
    controller = [[BWColorSearchAndReplaceController alloc]
                     initWithColorKey: colorKey];

    [NSApp beginSheet: [controller window]
           modalForWindow: [layerView window]
           modalDelegate: self
           didEndSelector: @selector(searchAndReplaceWindowDidEnd:returnCode:contextInfo:)
           contextInfo: controller];

} // searchAndReplaceColors


@end // BWGraphDocument

